use std::{
    fs::File, io::Read, path::{Path, PathBuf}, rc::Rc
};

use dj::{runtime::EnvExt, Env};

fn main() -> std::io::Result<()> {
    let files = enumerate_files()?;

    println!("Select a DJ program to run(index): ");

    let mut file_index = String::new();
    std::io::stdin().read_line(&mut file_index)?;
    let idx: usize = file_index.trim_end().parse().unwrap();

    let global = dj::Env::root();
    global.set_macro("println", builtin_println);

    let ret = global.eval(dj::parse(read_file(&files[idx])?).unwrap());

    match ret {
        Ok(ret) => println!("ret: {ret}"),
        Err(err) => println!("Error Detected -> {err:?}"),
    }

    Ok(())
}

fn enumerate_files() -> std::io::Result<Vec<PathBuf>> {
    let cur = std::env::current_dir()?.join("examples");

    let ans: Vec<PathBuf> = std::fs::read_dir(cur)?
        .filter(|entry| {
            if let Ok(entry) = entry {
                return entry.path().extension().unwrap() == "dj";
            }
            false
        })
        .map(|entry| entry.unwrap().path())
        .collect();

    for (idx, path) in ans.iter().enumerate() {
        println!("{}:{:?}", idx, path.file_name().unwrap());
    }

    Ok(ans)
}

fn read_file<P: AsRef<Path>>(path: P) -> std::io::Result<String> {
    println!("Running: {}", path.as_ref().display());
    let mut src_file = File::open(path)?;
    let mut src = String::new();
    src_file.read_to_string(&mut src)?;
    Ok(src)
}

fn builtin_println(env: Rc<Env>, mut exprs: Vec<dj::Expr>) -> dj::EvalResult {
    let mut acc = if let Some(expr) = exprs.pop() {
        env.eval(expr)?.to_string()
    } else {
        return Ok(dj::Value::Nil);
    };

    while let Some(expr) = exprs.pop() {
        acc.push_str(&env.eval(expr)?.to_string());
    }

    println!("{acc}");
    Ok(dj::Value::Nil)
}